イベントフローの確認

  • 2010 年 1月 15 日
  • kosuke

普段気にすることはありませんが、addEventListenerのuseCaptureについて。
AS3ではイベントが3段階で処理されている。原則的にイベントはステージから、イベントが発生したオブジェクトに向かい、オブジェクトに辿り着いたら今度はステージに戻ってくる。
入れ子になった表示オブジェクトは、この経路にしたがってイベントをリレーしているわけだ。
普段意識せず、useCapture = false のままに設定しているなら、イベントが発生したオブジェクトの段階か、ステージに戻ってくる段階の通知をキャッチしている。

このことを示した図がリファレンスに書かれている。
イベントフロー

この図はとてもよく説明されているのだけど、実際に動いているもので確認したくて試しました。
Eventオブジェクトには、イベントのリレーを止める、stopPropagationとstopImmediatePropagationってメソッドもあるので、この二つの挙動についても試している。

イベントフローを意識することがあるのは、入れ子になった表示オブジェクトの場合が想定されるので、大きい方から順番にA、B、Cと円を定義して、入れ子にする。

var A:Sprite	= new Sprite();
A.name = "A";
var B:Sprite	= new Sprite();
B.name = "B";
var C:Sprite	= new Sprite();
C.name = "C";
 
A.graphics.lineStyle(5, 0x000000 );
A.graphics.beginFill( 0x000000, 0 );
A.graphics.drawCircle( 0,0, 100 );
 
B.graphics.lineStyle(2.5, 0x404040 );
B.graphics.beginFill( 0x404040, 0 );
B.graphics.drawCircle( 0,0, 75 );
 
C.graphics.lineStyle(1, 0x808080 );
C.graphics.beginFill( 0x808080, 0 );
C.graphics.drawCircle( 0,0, 50 );
 
//BはAの子、CはBの子
A.addChild( B );
B.addChild( C );
addChild( A );

全ての円にキャプチャ段階とターゲット・バブリング段階それぞれにリスナーを定義します。
stopPropagation、stopImmediatePropagationを試すべく、優先順位をつけてリスナーを二つ登録しました。
優先順位の高い方のリスナーは、ラジオボタンに応じてstopPropagation、stopImmediatePropagationが実行されるようにします。
リスナーはテキストエリアに処理された内容を表示します。

//優先度の高いリスナー
A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true );
B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true );
C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true );
 
A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false );
B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false );
C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false );
 
//優先度の低いリスナー
A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 );
B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 );
C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 );
 
A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 );
B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 );
C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 );
 
 
private function _onMouseDownCaptureing1(e:MouseEvent):void{
	if( group1.selectedData == 1 ) e.stopPropagation();
	else if( group1.selectedData == 2 ) e.stopImmediatePropagation();
	var str:String = "#1 " + e.currentTarget["name"] + "がキャプチャ段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e) + "で処理。";
	_field.appendText( str + "\n" );
}
 
private function _onMouseDownTarget1(e:MouseEvent):void{
	if( group2.selectedData == 1 ) e.stopPropagation();
	else if( group2.selectedData == 2 ) e.stopImmediatePropagation();
	var str:String = "#1 " + e.currentTarget["name"] + "がターゲット・バブリング段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e)  + "で処理。";
	_field.appendText( str + "\n" );
}
 
private function _onMouseDownCaptureing2(e:MouseEvent):void{
	var str:String = "#2 " + e.currentTarget["name"] + "がキャプチャ段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e)  + "で処理。";
	_field.appendText( str + "\n" );
}
 
private function _onMouseDownTarget2(e:MouseEvent):void{
	var str:String = "#2 " + e.currentTarget["name"] + "がターゲット・バブリング段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e)  + "で処理。";
	_field.appendText( str + "\n" );
}
 
private function _returnPhase(e:MouseEvent):String{
	return ( EventPhase.AT_TARGET == e.eventPhase ) ? "ターゲット段階" : ( EventPhase.BUBBLING_PHASE == e.eventPhase ) ? "バブリング段階" : "キャプチャ段階";
}

こちらで試せます。場合によっては意図に反して2回処理されていたみたいな間違いはありそうだね。
こんなものを意識して何か作ることが無いことを祈るばかり。

This movie requires Flash Player 10.0.0

“イベントフローの確認” に コメントはありません

コメントをどうぞ